package org.smart.access.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.mindrot.jbcrypt.BCrypt;
import org.smart.access.dto.PageProp;
import org.smart.access.dto.UserInfo;
import org.smart.access.dto.UserRegister;
import org.smart.access.model.House;
import org.smart.access.service.HouseService;
import org.smart.access.service.UserService;
import org.smart.access.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.smart.access.model.User;

import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * UserServiceImpl
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Lazy
    @Autowired
    private HouseService houseService;

    @Override
    public Long register(UserRegister userRegister, boolean isByAdmin) throws SQLException {
        // 判复
        if (exists(new LambdaQueryWrapper<User>()
                .eq(User::getPhone, userRegister.getPhone()))) {
            throw new SQLIntegrityConstraintViolationException();
        }
        House house = houseService.getOne(new LambdaQueryWrapper<House>()
                .select(House::getId)
                .eq(House::getBuilding, userRegister.getBuilding())
                .eq(House::getRoom, userRegister.getRoom())
        );
        // 判房屋是否不存在
        if (null == house) {
            // 判非业主
            if (!userRegister.getResidentNature().equals("业主")) {
                // 房屋不存在且不是业主
                throw new RuntimeException("房屋为注册");
            } else {
                // 不存在, 但是业主, 新增房屋
                house = House.builder()
                        .building(userRegister.getBuilding())
                        .room(userRegister.getRoom())
                        .build();
                houseService.save(house);   // 保存后会自动注入id
            }
        }

        User user = User.builder()
                .houseId(house.getId())
                .realname(userRegister.getRealname())
                .phone(userRegister.getPhone())
                .gender(userRegister.getGender())
                .residentNature(userRegister.getResidentNature())
//                .role(1)  // 数据库有默认值为1
                .build();
        // 判业主
        if (userRegister.getResidentNature().equals("业主")) {
            // 业主可登录, 所以需要设置密码, 将默认值设为手机号后六位
            String password = userRegister.getPhone().substring(userRegister.getPhone().length() - 6);
            // 密码加密
            String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
            user.setPassword(hashedPassword);
        }

        // 判创建者
        if (isByAdmin) {
            // 管理员创建 直接可用
            user.setRole(1);
        } else {
            user.setRole(2);
        }

        // 保存用户
        save(user);
        return user.getUid();
    }

    @Override
    public Long loginWithPwd(String phone, String password) {
        User user = getOne(new LambdaQueryWrapper<User>()
                .select(User::getUid, User::getPassword)
                .eq(User::getPhone, phone)
                .and(i -> i
                        .eq(User::getResidentNature, "业主")
                        .or().eq(User::getRole, 0)
                ));

        if (user != null && BCrypt.checkpw(password, user.getPassword())) {
            return user.getUid();
        }

        return 0L;
    }

    @Override
    public UserInfo getInfo(Long userId) {
        User user = getById(userId);
        House house = user.getRole() == 0 ? new House() : houseService.getById(user.getHouseId());
        String ownerName = user.getRole() == 0 ? "" : getOne(new LambdaQueryWrapper<User>()
                .select(User::getRealname)
                .eq(User::getHouseId, house.getId())
                .eq(User::getResidentNature, "业主")
        ).getRealname();
        return UserInfo.of(user, house, ownerName);
    }

    @Override
    public IPage<UserInfo> getMUserList(PageProp<User, Void> pageProp) {
        // 分页参数
        IPage<User> userPage = new Page<>(pageProp.getCurrent(), pageProp.getSize());
        // 查询参数
        QueryWrapper<User> userQw = new QueryWrapper<User>(pageProp.getSearch()).eq("role", 1);
        // 为了支持房屋查询, 需要关联查询
        QueryWrapper<House> houseQw = new QueryWrapper<House>();
        // 查询参数
        if (pageProp.getLikeAll() != null && !pageProp.getLikeAll().isEmpty()) {
            userQw.and(i -> i
                            .like("realname", pageProp.getLikeAll())
                            .or().like("phone", pageProp.getLikeAll())
                            .or().like("resident_nature", pageProp.getLikeAll())
                    );
                    houseQw.and(i -> i
                            .like("building", pageProp.getLikeAll())
                            .or().like("room", pageProp.getLikeAll())
                    );
        }
        Map<Long, House> houseMap = null;
        // 判是否存在房屋条件
        if (houseQw.isNonEmptyOfWhere()) {
            houseMap = houseService.list(houseQw).stream()
                    .collect(Collectors.toMap(House::getId, house -> house));
            // 为用户添加查询条件
            userQw.or().in("house_id", houseMap.keySet());
        }
        // 查询用户
        userPage = page(userPage, userQw);
        // 判断是否先查了 房屋
        if (houseMap == null) {
            // 查询房屋
            houseMap = houseService.list(houseQw
                    .in("id", userPage.getRecords().stream()
                                    .map(User::getHouseId)
                                    .collect(Collectors.toList())))
                    .stream()
                    .collect(Collectors.toMap(House::getId, house -> house));
        }

        Map<Long, House> finalHouseMap = houseMap;
        return userPage.convert(user -> UserInfo.of(user, finalHouseMap.get(user.getHouseId()), null));
    }
}
